﻿using Dotnet.Utils.Utility.EncryptUtil;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Dotnet.Utils.Utility.StreamUtil
{
    /// <summary>
    /// 流扩展
    /// </summary>
    public static class StreamExtensions
    {
        /// <summary>
        /// 将 Stream 转成 byte[]
        /// </summary>
        /// <param name="bytes"></param>
        /// <returns></returns>
        public static byte[] StreamToBytes(Stream stream)
        {
            byte[] bytes = new byte[stream.Length];
            stream.Read(bytes, 0, bytes.Length);
            stream.Seek(0, SeekOrigin.Begin);
            stream.Flush();
            stream.Close();
            return bytes;
        }

        /// <summary>
        /// 将 byte[] 转成 Stream
        /// </summary>
        /// <param name="bytes"></param>
        /// <returns></returns>
        public static Stream BytesToStream(byte[] bytes)
        {
            return new MemoryStream(bytes);
        }

        /// <summary>
        /// 将字符串 转成 byte[]
        /// </summary>
        /// <param name="bytes"></param>
        /// <returns></returns>
        public static byte[] StringToBytes(string str)
        {
            UnicodeEncoding converter = new UnicodeEncoding();
            byte[] bytes = converter.GetBytes(str);
            string inputString = converter.GetString(bytes);

            inputString = System.Convert.ToBase64String(bytes);
            bytes = System.Convert.FromBase64String(inputString);
            return bytes;
        }

        /// <summary>
        /// 写文件流
        /// </summary>
        /// <param name="fileBytes">文件流</param>
        /// <param name="FullName">文件完全路径+文件名（包括后缀名）</param>
        public static void SaveFileByStream(byte[] fileBytes, string FullName)
        {
            File.WriteAllBytes(FullName, fileBytes);
        }

        /// <summary>
        /// 写文件流
        /// </summary>
        /// <param name="stream">文件流</param>
        /// <param name="FullName">文件完全路径+文件名（包括后缀名）</param>
        public static void StreamToFile(Stream stream, string fileName)
        {
            // 把 Stream 转换成 byte[]
            byte[] bytes = new byte[stream.Length];
            stream.Read(bytes, 0, bytes.Length);
            // 设置当前流的位置为流的开始
            stream.Seek(0, SeekOrigin.Begin);
            // 把 byte[] 写入文件
            FileStream fs = new FileStream(fileName, FileMode.Create);
            BinaryWriter bw = new BinaryWriter(fs);
            bw.Write(bytes);
            bw.Close();
            fs.Close();
        }

        /// <summary>
        /// 读文件流
        /// </summary>
        /// <param name="fileFullName">文件地址路径（路径+文件名称+文件后缀）</param>
        /// <returns></returns>
        public static Stream FileToStream(string fileFullName)
        {
            // 打开文件  
            using (FileStream fileStream = new FileStream(fileFullName, FileMode.Open, FileAccess.Read, FileShare.Read))
            {
                // 读取文件的 byte[]  
                byte[] bytes = new byte[fileStream.Length];
                fileStream.Read(bytes, 0, bytes.Length);
                return new MemoryStream(bytes);
            }
        }

        public static byte[] GetAllBytes(this Stream stream)
        {
            if (stream is MemoryStream memoryStream)
            {
                return memoryStream.ToArray();
            }

            using (var ms = stream.CreateMemoryStream())
            {
                return ms.ToArray();
            }
        }

        public static async Task<byte[]> GetAllBytesAsync(this Stream stream, CancellationToken cancellationToken = default)
        {
            if (stream is MemoryStream memoryStream)
            {
                return memoryStream.ToArray();
            }

            using (var ms = await stream.CreateMemoryStreamAsync(cancellationToken))
            {
                return ms.ToArray();
            }
        }

        public static Task CopyToAsync(this Stream stream, Stream destination, CancellationToken cancellationToken)
        {
            if (stream.CanSeek)
            {
                stream.Position = 0;
            }

            return stream.CopyToAsync(
                destination,
                81920, //this is already the default value, but needed to set to be able to pass the cancellationToken
                cancellationToken
            );
        }

        public static async Task<MemoryStream> CreateMemoryStreamAsync(this Stream stream, CancellationToken cancellationToken = default)
        {
            if (stream.CanSeek)
            {
                stream.Position = 0;
            }

            var memoryStream = new MemoryStream();
            await stream.CopyToAsync(memoryStream, cancellationToken);

            if (stream.CanSeek)
            {
                stream.Position = 0;
            }

            memoryStream.Position = 0;
            return memoryStream;
        }

        public static MemoryStream CreateMemoryStream(this Stream stream)
        {
            if (stream.CanSeek)
            {
                stream.Position = 0;
            }

            var memoryStream = new MemoryStream();
            stream.CopyTo(memoryStream);

            if (stream.CanSeek)
            {
                stream.Position = 0;
            }

            memoryStream.Position = 0;
            return memoryStream;
        }

        /// <summary>
        /// 转成byte数组
        /// </summary>
        /// <param name="stream"></param>
        /// <returns></returns>
        public static byte[] ToArray(this Stream stream)
        {
            stream.Position = 0;
            var bytes = new byte[stream.Length];
            _ = stream.Read(bytes, 0, bytes.Length);
            // 设置当前流的位置为流的开始
            stream.Seek(0, SeekOrigin.Begin);
            return bytes;
        }

        /// <summary>
        /// 流洗码，在流的末端随即增加几个空字节，重要数据请谨慎使用，可能造成流损坏
        /// </summary>
        /// <param name="stream"></param>
        public static void ShuffleCode(this Stream stream)
        {
            if (stream is not { CanWrite: true, CanSeek: true }) return;
            var position = stream.Position;
            stream.Position = stream.Length;
            for (var i = 0; i < new Random().Next(1, 20); i++)
            {
                stream.WriteByte(0);
            }
            stream.Flush();
            stream.Position = position;
        }

        /// <summary>
        /// 读取所有行
        /// </summary>
        /// <param name="stream"></param>
        /// <param name="closeAfter">读取完毕后关闭流</param>
        /// <returns></returns>
        public static List<string> ReadAllLines(this StreamReader stream, bool closeAfter = true)
        {
            var stringList = new List<string>();
            string str;
            while (!string.IsNullOrWhiteSpace(str = stream.ReadLine() ?? string.Empty))
            {
                stringList.Add(str);
            }
            if (!closeAfter) return stringList;
            stream.Close();
            stream.Dispose();
            return stringList;
        }

        /// <summary>
        /// 读取所有行
        /// </summary>
        /// <param name="stream"></param>
        /// <param name="encoding"></param>
        /// <param name="closeAfter">读取完毕后关闭流</param>
        /// <returns></returns>
        public static List<string> ReadAllLines(this FileStream stream, Encoding encoding, bool closeAfter = true)
        {
            var stringList = new List<string>();
            string str;
            var sr = new StreamReader(stream, encoding);
            while (!string.IsNullOrWhiteSpace(str = sr.ReadLine() ?? string.Empty))
            {
                stringList.Add(str);
            }
            if (!closeAfter) return stringList;
            sr.Close();
            sr.Dispose();
            stream.Close();
            stream.Dispose();
            return stringList;
        }

        /// <summary>
        /// 读取所有文本
        /// </summary>
        /// <param name="stream"></param>
        /// <param name="encoding"></param>
        /// <param name="closeAfter">读取完毕后关闭流</param>
        /// <returns></returns>
        public static string ReadAllText(this FileStream stream, Encoding encoding, bool closeAfter = true)
        {
            var sr = new StreamReader(stream, encoding);
            var text = sr.ReadToEnd();
            if (!closeAfter) return text;
            sr.Close();
            sr.Dispose();
            stream.Close();
            stream.Dispose();
            return text;
        }

        /// <summary>
        /// 写入所有文本
        /// </summary>
        /// <param name="stream"></param>
        /// <param name="content"></param>
        /// <param name="encoding"></param>
        /// <param name="closeAfter">读取完毕后关闭流</param>
        /// <returns></returns>
        public static void WriteAllText(this FileStream stream, string content, Encoding encoding, bool closeAfter = true)
        {
            var sw = new StreamWriter(stream, encoding);
            stream.SetLength(0);
            sw.Write(content);
            if (!closeAfter) return;
            sw.Close();
            sw.Dispose();
            stream.Close();
            stream.Dispose();
        }

        /// <summary>
        /// 写入所有文本行
        /// </summary>
        /// <param name="stream"></param>
        /// <param name="lines"></param>
        /// <param name="encoding"></param>
        /// <param name="closeAfter">读取完毕后关闭流</param>
        /// <returns></returns>
        public static void WriteAllLines(this FileStream stream, IEnumerable<string> lines, Encoding encoding, bool closeAfter = true)
        {
            var sw = new StreamWriter(stream, encoding);
            stream.SetLength(0);
            foreach (var line in lines)
            {
                sw.WriteLine(line);
            }
            sw.Flush();
            if (!closeAfter) return;
            sw.Close();
            sw.Dispose();
            stream.Close();
            stream.Dispose();
        }

        /// <summary>
        /// 共享读写打开文件
        /// </summary>
        /// <param name="file"></param>
        /// <returns></returns>
        public static FileStream ShareReadWrite(this FileInfo file) => file.Open(FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite);

        /// <summary>
        /// 读取所有行
        /// </summary>
        /// <param name="stream"></param>
        /// <param name="closeAfter">读取完毕后关闭流</param>
        /// <returns></returns>
        public static async Task<List<string>> ReadAllLinesAsync(this StreamReader stream, bool closeAfter = true)
        {
            var stringList = new List<string>();
            string str;
            while (!string.IsNullOrWhiteSpace(str = await stream.ReadLineAsync().ConfigureAwait(false) ?? string.Empty))
            {
                stringList.Add(str);
            }
            if (!closeAfter) return stringList;
            stream.Close();
            stream.Dispose();
            return stringList;
        }

        /// <summary>
        /// 读取所有行
        /// </summary>
        /// <param name="stream"></param>
        /// <param name="encoding"></param>
        /// <param name="closeAfter">读取完毕后关闭流</param>
        /// <returns></returns>
        public static async Task<List<string>> ReadAllLinesAsync(this FileStream stream, Encoding encoding, bool closeAfter = true)
        {
            var stringList = new List<string>();
            string str;
            var sr = new StreamReader(stream, encoding);
            while (!string.IsNullOrWhiteSpace(str = await sr.ReadLineAsync().ConfigureAwait(false) ?? string.Empty))
            {
                stringList.Add(str);
            }
            if (!closeAfter) return stringList;
            sr.Close();
            sr.Dispose();
            stream.Close();
            await stream.DisposeAsync().ConfigureAwait(false);
            return stringList;
        }

        /// <summary>
        /// 读取所有文本
        /// </summary>
        /// <param name="stream"></param>
        /// <param name="encoding"></param>
        /// <param name="closeAfter">读取完毕后关闭流</param>
        /// <returns></returns>
        public static async Task<string> ReadAllTextAsync(this FileStream stream, Encoding encoding, bool closeAfter = true)
        {
            var sr = new StreamReader(stream, encoding);
            var text = await sr.ReadToEndAsync().ConfigureAwait(false);
            if (!closeAfter) return text;
            sr.Close();
            sr.Dispose();
            stream.Close();
            await stream.DisposeAsync().ConfigureAwait(false);
            return text;
        }

        /// <summary>
        /// 写入所有文本
        /// </summary>
        /// <param name="stream"></param>
        /// <param name="content"></param>
        /// <param name="encoding"></param>
        /// <param name="closeAfter">读取完毕后关闭流</param>
        /// <returns></returns>
        public static async Task WriteAllTextAsync(this FileStream stream, string content, Encoding encoding, bool closeAfter = true)
        {
            var sw = new StreamWriter(stream, encoding);
            stream.SetLength(0);
            await sw.WriteAsync(content).ConfigureAwait(false);
            await sw.FlushAsync().ConfigureAwait(false);
            if (closeAfter)
            {
                sw.Close();
                stream.Close();
                await sw.DisposeAsync().ConfigureAwait(false);
                await stream.DisposeAsync().ConfigureAwait(false);
            }
        }

        /// <summary>
        /// 写入所有文本行
        /// </summary>
        /// <param name="stream"></param>
        /// <param name="lines"></param>
        /// <param name="encoding"></param>
        /// <param name="closeAfter">读取完毕后关闭流</param>
        /// <returns></returns>
        public static async Task WriteAllLinesAsync(this FileStream stream, IEnumerable<string> lines, Encoding encoding, bool closeAfter = true)
        {
            var sw = new StreamWriter(stream, encoding);
            stream.SetLength(0);
            foreach (var line in lines)
            {
                await sw.WriteLineAsync(line).ConfigureAwait(false);
            }
            await sw.FlushAsync().ConfigureAwait(false);
            if (closeAfter)
            {
                sw.Close();
                stream.Close();
                await sw.DisposeAsync().ConfigureAwait(false);
                await stream.DisposeAsync().ConfigureAwait(false);
            }
        }

        /// <summary>
        /// </summary>
        /// <param name="stream"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public static async Task<byte[]> ToArrayAsync(this Stream stream, CancellationToken cancellationToken = default)
        {
            stream.Position = 0;
            var bytes = new byte[stream.Length];
            _ = await stream.ReadAsync(bytes, cancellationToken);
            stream.Seek(0, SeekOrigin.Begin);
            return bytes;
        }

        public static string ReadToEnd(this Stream stream, Encoding encoding = null, int bufferSize = 2048, bool isCloseStream = true)
        {
            string result;
            if (stream == null)
            {
                result = string.Empty;
            }
            else
            {
                if (encoding == null)
                {
                    encoding = Encoding.UTF8;
                }
                if (!stream.CanRead)
                {
                    result = string.Empty;
                }
                else
                {
                    using (StreamReader streamReader = new StreamReader(stream, encoding, true, bufferSize, !isCloseStream))
                    {
                        if (stream.CanSeek)
                        {
                            stream.Seek(0L, SeekOrigin.Begin);
                        }
                        string text = streamReader.ReadToEnd();
                        if (stream.CanSeek)
                        {
                            stream.Seek(0L, SeekOrigin.Begin);
                        }
                        result = text;
                    }
                }
            }
            return result;
        }

        public static byte[] ToByteArray(this Stream @this)
        {
            byte[] result;
            using (MemoryStream memoryStream = new MemoryStream())
            {
                @this.CopyTo(memoryStream);
                result = memoryStream.ToArray();
            }
            return result;
        }

        public static string ToMD5Hash(this Stream @this)
        {
            return SecurityUtil.Md5Hash(@this);
        }
    }
}